package com.zxhhyj.storm.schema

import kotlin.reflect.KClass
import kotlin.reflect.KProperty1

typealias ColumnBindingProperty<T, V> = KProperty1<T, V>

enum class DataType {
    NULL, INTEGER, REAL, TEXT, BLOB
}

sealed interface Column<T, A> {
    val fieldName: String
    val kotlinType: KClass<*>
    val dataType: DataType
    val isPrimaryKey: Boolean
    val isAutoincrement: Boolean
    val isNull: Boolean
    val isUnique: Boolean
    val defaultProvider: (() -> A)?
    val converter: Transform<A, *>?

    data class Transform<A, R>(val to: (A) -> R, val form: (R) -> A)

    data class RowBinding<T, A>(
        override val fieldName: String,
        override val kotlinType: KClass<*>,
        override val dataType: DataType,
        override val isPrimaryKey: Boolean,
        override val isAutoincrement: Boolean,
        override val isNull: Boolean,
        override val isUnique: Boolean,
        override val defaultProvider: (() -> A)?,
        override val converter: Transform<A, *>?,
        val selector: ColumnBindingProperty<T, A>
    ) : Column<T, A>
}

class ColumnBuilder<T, A>(
    override val fieldName: String,
    override val kotlinType: KClass<*>,
    override val dataType: DataType,
) : Column<T, A> {
    override var isPrimaryKey: Boolean = false
    override var isAutoincrement: Boolean = false
    override var isNull: Boolean = false
    override var isUnique: Boolean = false
    override var defaultProvider: (() -> A)? = null
    override var converter: Column.Transform<A, *>? = null

    fun primaryKey(autoincrement: Boolean = false): ColumnBuilder<T, A> {
        isPrimaryKey = true
        isAutoincrement = autoincrement
        return this
    }

    @Suppress("UNCHECKED_CAST")
    fun nullable(): ColumnBuilder<T, A?> {
        isNull = true
        return this as ColumnBuilder<T, A?>
    }

    fun unique(): ColumnBuilder<T, A> {
        isUnique = true
        return this
    }

    fun default(value: A): ColumnBuilder<T, A?> {
        return default {
            value
        }
    }

    @Suppress("UNCHECKED_CAST")
    fun default(block: () -> A): ColumnBuilder<T, A?> {
        defaultProvider = block
        return this as ColumnBuilder<T, A?>
    }

    @Suppress("UNCHECKED_CAST")
    fun <R> transform(to: (A) -> R, form: (R) -> A): ColumnBuilder<T, R> {
        converter = Column.Transform(to, form)
        return this as ColumnBuilder<T, R>
    }

    companion object {
        inline operator fun <T, reified A> invoke(fieldName: String, dataType: DataType): ColumnBuilder<T, A> {
            return ColumnBuilder(fieldName = fieldName, kotlinType = A::class, dataType = dataType)
        }
    }
}